package pl.edu.agh.agents.agent;

import pl.edu.agh.agents.exception.PathNotFoundException;
import pl.edu.agh.agents.path.Path;
import pl.edu.agh.agents.planner.CentralPlanner;
import pl.edu.agh.agents.state.State;
import pl.edu.agh.agents.state.StateSpace;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamOmitField;

@XStreamAlias("Agent")
public abstract class Agent {
	protected int ID;
	protected State currentState;
	protected State goalState;
	@XStreamOmitField
	protected State startState;
	@XStreamOmitField
	protected Path path = new Path();
	protected AgentStrategy strategy;
	protected int lookAheadStates = 1;
	protected boolean setPathInStateSpace;

	abstract public Path getBestPath();
	abstract public void makeMove() throws PathNotFoundException;
	abstract protected void markGoalState();

	public int getID() {
		return ID;
	}

	// we use empty constructor in newInstance(), so we have to have setID()
	// method
	public void setID(int ID) {
		this.ID = ID;
	}

	public void setStartState(State start) {
		this.startState = start;
	}

	public State getCurrentState() {
		return currentState;
	}

	public void setCurrentState(State currentState) {
		this.currentState = currentState;
	}

	public State getFutureState(int iterationNumber){
		if(path == null || iterationNumber<0){
			return null;
		}
		if(path.size()<=iterationNumber){
			return goalState;
		}
		return path.get(path.indexOf(currentState) + iterationNumber);
	}

	public void setGoalState(State goalState) {
		SystemManager.getInstance().agentGoalStateChanged(this);
		this.goalState = goalState;
	}

	public State getGoalState() {
		return goalState;
	}

	public Path getPath() {
		return path;
	}

	public AgentStrategy getStrategy() {
		return strategy;
	}
	public void setStrategy(AgentStrategy strategy) {
		this.strategy = strategy;
	}

	public int getLookAheadStates() {
		return lookAheadStates;
	}

	public void setLookAheadStates(int lookAheadStates) {
		this.lookAheadStates = lookAheadStates;
	}

	public void setSetPathInStateSpace(boolean setPathInStateSpace) {
		this.setPathInStateSpace = setPathInStateSpace;
	}

	protected void update(State oldState, State newState, Path newPath,
			boolean setPathInStateSpace, boolean setPathInCentralPlanner) {
		if(!update(oldState, newState, newPath)){
			return;
		}
		if (setPathInStateSpace) {
			StateSpace.getInstance().updatePath(this, path);
		}
		if (setPathInCentralPlanner) {
			CentralPlanner.getInstance().updatePath(this, path);
		}
	}

	protected boolean update(State oldState, State newState, Path newPath) {
		if(StateSpace.getInstance().updateState(this, newState)){
			this.path = newPath;
			oldState.setAgent(null);
			currentState = newState;
			currentState.setAgent(this);
			// MARK STATE IF AGENT ACHIVED GOAL STATE
			if (currentState.equals(goalState)) {
				this.markGoalState();
			}
			return true;
		}
		return false;
	}

	protected boolean isNextStateAvailable() {
		return path!=null && path.size()>1 && path.get(1).isEmpty();
	}

	protected boolean isAheadStateAvailable() { 
		if(path!=null){		
			int size = Math.min(path.size()-1, lookAheadStates);
			for(int i=1; i<size; i++){
				int agentsNumberInAheadState = SystemManager.getInstance().getStatesAvailability(i, path.get(i+1));
				if(agentsNumberInAheadState > 0){
					return false;
				}
			}
			return true;
		}
		return false;
	}

	public int isAheadStateInAgentPath(int iteration, State aheadState){
		if(path!=null){
			if(iteration > 0 && path.size()>iteration && path.get(iteration).equals(aheadState)){
				return 1;
			}
			//			if(path.size()<=iteration && goalState.equals(aheadState)){
			//				return 1;
			//			}
		} 
		return 0;
	}
}
